package com.example.yin.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.yin.common.R;
import com.example.yin.mapper.CollectMapper;
import com.example.yin.model.domain.Collect;
import com.example.yin.model.domain.Consumer;
import com.example.yin.model.domain.Recommend;
import com.example.yin.model.domain.Song;
import com.example.yin.model.request.CollectRequest;
import com.example.yin.service.CollectService;
import com.example.yin.service.ConsumerService;
import com.example.yin.service.SongService;
import org.apache.mahout.cf.taste.impl.common.FastByIDMap;
import org.apache.mahout.cf.taste.impl.model.GenericDataModel;
import org.apache.mahout.cf.taste.impl.model.GenericPreference;
import org.apache.mahout.cf.taste.impl.model.GenericUserPreferenceArray;
import org.apache.mahout.cf.taste.impl.recommender.GenericItemBasedRecommender;
import org.apache.mahout.cf.taste.impl.similarity.UncenteredCosineSimilarity;
import org.apache.mahout.cf.taste.model.DataModel;
import org.apache.mahout.cf.taste.model.Preference;
import org.apache.mahout.cf.taste.model.PreferenceArray;
import org.apache.mahout.cf.taste.recommender.RecommendedItem;
import org.apache.mahout.cf.taste.recommender.Recommender;
import org.apache.mahout.cf.taste.similarity.ItemSimilarity;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

@Service
public class CollectServiceImpl extends ServiceImpl<CollectMapper, Collect> implements CollectService {
    @Autowired
    private CollectMapper collectMapper;

    @Autowired
    private SongService songService;

    @Autowired
    private ConsumerService consumerService;

    // 推荐歌曲数
    public static int recommendNum = 5;

    @Override
    public R addCollection(CollectRequest addCollectRequest) {
        //作者用type来判断收藏的是歌还是歌单
        Collect collect = new Collect();
        BeanUtils.copyProperties(addCollectRequest, collect);
        if (collectMapper.insert(collect) > 0) {
            return R.success("收藏成功", true);
        } else {
            return R.error("收藏失败");
        }
    }

    @Override
    public R existSongId(CollectRequest isCollectRequest) {
        QueryWrapper<Collect> queryWrapper = new QueryWrapper();
        queryWrapper.eq("user_id", isCollectRequest.getUserId());
        queryWrapper.eq("song_id", isCollectRequest.getSongId());
        if (collectMapper.selectCount(queryWrapper) > 0) {
            return R.success("已收藏", true);
        } else {
            return R.success("未收藏", false);
        }
    }

    @Override
    public R deleteCollect(Integer userId, Integer songId) {
        QueryWrapper<Collect> queryWrapper = new QueryWrapper();
        queryWrapper.eq("user_id", userId);
        queryWrapper.eq("song_id", songId);
        if (collectMapper.delete(queryWrapper) > 0) {
            return R.success("取消收藏", false);
        } else {
            return R.error("取消收藏失败");
        }
    }

    @Override
    public R collectionOfUser(Integer userId) {
        QueryWrapper<Collect> queryWrapper = new QueryWrapper();
        queryWrapper.eq("user_id", userId);
        return R.success("用户收藏", collectMapper.selectList(queryWrapper));
    }

    /**
     * 协同过滤算法
     *
     * @param userId
     * @return 推荐歌曲
     */
    @Override
    public R recommendSongList(Integer userId) {
        if (null == userId || userId <= 0)
            return R.error("请先登录!");

        Consumer consumer = consumerService.getById(userId);
        if (null == consumer || consumer.getId() <= 0)
            return R.error("用户不存在");
        // 查询所有用户的收藏数据
        List<Collect> list = list();
        //
        List<Recommend> recommendAll = new ArrayList<Recommend>();
        List<Long> itemIds = new ArrayList<Long>();
        for (Collect c : list) {
            Recommend r = new Recommend();
            r.setUserId(c.getUserId().longValue());
            r.setItemId(c.getSongId().longValue());
            r.setCommend(1);// 每个用户收藏的次数只有1
            recommendAll.add(r);
        }
        // 获取数据模型
        DataModel dataModel = getDataModel(recommendAll);
        if (null == dataModel)
            return null;
        // 获取推荐歌曲id
        itemIds = baseItem(userId, dataModel);
        if (itemIds == null)
            return null;
        // 根据推荐歌曲id查询歌曲信息
        List<Song> ls = new ArrayList<>();
        for (Long sid : itemIds) {
            Song song = songService.getById(sid);
            ls.add(song);
        }
        if (ls.size() == 0)
            return R.error("收藏歌曲太少无法推荐,快去收藏你的专属歌曲吧!");
        return R.success("获取推荐歌曲成功!", ls);
    }

    /**
     * 根据数据模型推荐歌曲
     *
     * @param userId
     * @param model
     * @return
     */
    private List<Long> baseItem(Integer userId, DataModel model) {
        if (model == null)
            return null;
        try {
            // 注册推荐器
            ItemSimilarity similarity = new UncenteredCosineSimilarity(model);
            // 初始化推荐器
            Recommender recommender = new GenericItemBasedRecommender(model, similarity);
            // 获取推荐的个数
            List<RecommendedItem> items = recommender.recommend(userId.longValue(), recommendNum);
            List<Long> res = new ArrayList<>();
            for (RecommendedItem ri : items) {
                long songId = ri.getItemID();
                res.add(songId);
            }
            return res;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * 构造数据模型
     *
     * @param recommendAll
     * @return 构造好的数据模型
     */
    private DataModel getDataModel(List<Recommend> recommendAll) {
        if (null == recommendAll || recommendAll.size() == 0) {
            System.out.println("用户未收藏歌曲,请收藏后重试!");
            return null;
        }

        // 用用户id当做key,歌曲id当做value,存入map集合形成数据矩阵
        Map<Long, List<Preference>> map = new HashMap<>();
        for (Recommend r : recommendAll) {
            List<Preference> preferenceList = new ArrayList<>();
            Long userId = r.getUserId();
            Long itemId = r.getItemId();
            Integer commend = r.getCommend();
            Integer preference = 0;
            if (commend != 0) {
                preference = commend;
                preferenceList.add(new GenericPreference(userId, itemId, preference));
                if (map.containsKey(userId)) {
                    List<Preference> preferenceTemp = map.get(userId);
                    preferenceTemp.addAll(preferenceList);
                    map.put(userId, preferenceTemp);
                } else {
                    map.put(userId, preferenceList);
                }
            }
        }

        FastByIDMap<PreferenceArray> preferenceArray = new FastByIDMap<>();
        Set<Long> set = map.keySet();
        for (Long uid : set) {
            List<Preference> preList = map.get(uid);
            preferenceArray.put(uid, new GenericUserPreferenceArray(preList));
        }

        DataModel model = new GenericDataModel(preferenceArray);

        return model;
    }
}
